/*
 * Copyright (c) 2011, Marc-Andre Ferland - madrang@gmail.com
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the The Warrent Team nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *     * This software is free for non-commercial use. You may not use this
 *       software, in whole or in part, in support of any commercial product
 *       without the express consent of the author.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE WARRENT TEAM BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
using System;
using System.Text;
using System.Globalization;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;

namespace OsHelper.DebugTools
{
	public class Logger : LogService.iService
	{
		private static Logger log_Def;
		public static Logger Default
		{
			get {
				if(log_Def == null)
					log_Def = new Logger();
				
				return log_Def;
			}
		}
		
		public List<LogService.iService> Client { get; private set; }
		
		public Logger ()
		{
			this.Client = new List<LogService.iService>();
		}
		
		///<summary>
		///   Writes a formatted string with a specified warning level
		///   to the log file, if one exists.
		///</summary>
		///<param name="level">
		///   A <see cref="LogLevel" /> containing the severity of the
		///   message.
		///</param>
		///<param name="message">
		///   A <see cref="string" /> containing the message to write.
		///</param>
		///<remarks>
		///   <para>The message will only be written to the log if
		///   <see cref="Level" /> contains <paramref name="level" />.
		///   </para>
		///</remarks>
		public override void Write (LogLevel level, string message)
		{
			//Bug with this method and the one with params, DO NOT PUT CODE HERE.
			//In some case this method is called and sometimes the other while calling
			//Write(LogLevel, string);
			//Only use the other one.
			//If the interface could use the other one this one should be deleted.
			this.Write(level, message, new object[0]);
		}
		
		///<summary>
		///   Writes a formatted string with a specified warning level
		///   to the log file, if one exists.
		///</summary>
		///<param name="level">
		///   A <see cref="LogLevel" /> containing the severity of the
		///   message.
		///</param>
		///<param name="format">
		///   A <see cref="string" /> containing the format to use for
		///   the message.
		///</param>
		///<param name="args">
		///   A <see cref="object[]" /> containing values to insert
		///   into the format.
		///</param>
		///<remarks>
		///   <para>The message will only be written to the log if
		///   <see cref="Level" /> contains <paramref name="level" />.
		///   </para>
		///   <para>This method outputs using the current culture of
		///   the assembly. To use a different culture, use
		///   <see
		///   cref="Write(LogLevel,IFormatProvider,string,object[])"
		///   />.</para>
		///   <para>See <see cref="string.Format(string,object[])" />
		///   for more details on this method's arguments.</para>
		///</remarks>
		public void Write (LogLevel level, string FormatMsg, params object [] args)
		{
			if (args.Length > 0)
				FormatMsg = string.Format (FormatMsg, args);
			
			foreach (LogService.iService Item in this.Client)
			{
				Item.Write(level, FormatMsg);
			}
		}
		
		///<summary>
		///   Writes a formatted string with a specified warning level
		///   to the log file, if one exists.
		///</summary>
		///<param name="level">
		///   A <see cref="LogLevel" /> containing the severity of the
		///   message.
		///</param>
		///<param name="provider">
		///   A <see cref="IFormatProvider" /> object to use when
		///   formatting values for the message.
		///</param>
		///<param name="format">
		///   A <see cref="string" /> containing the format to use for
		///   the message.
		///</param>
		///<param name="args">
		///   A <see cref="object[]" /> containing values to insert
		///   into the format.
		///</param>
		///<remarks>
		///   <para>The message will only be written to the log if
		///   <see cref="Level" /> contains <paramref name="level" />.
		///   </para>
		///   <para>See <see
		///   cref="string.Format(IFormatProvider,string,object[])" />
		///   for more details on this method's arguments.</para>
		///</remarks>
		public void Write (LogLevel level, IFormatProvider provider,
		                          string format, params object [] args)
		{
			this.Write (level, string.Format (provider, format, args));
		}
		
		
		private string AddSender (string message)
		{
			string Result;
			StackTrace stackTrace = new StackTrace();
				Result = string.Format ("{0}: {1}",
				                        stackTrace.GetFrame(2).GetMethod().Name,
				                        message);
			return Result;
		}
		private string AddSender<TSender> (string message)
		{
			return string.Format ("{0}: {1}",
			                      typeof (TSender).Name,
			                      message);
		}
		
		///<summary>
		/// Debug information that will only need to be seen when tracking down specific problems.
		///</summary>
		public void Debug (string msg, params object [] args)
		{
			this.Write (LogLevel.Debug, AddSender (msg), args);
		}
		///<summary>
		/// Debug information that will only need to be seen when tracking down specific problems.
		///</summary>
		public void Debug<TSender> (string msg, params object [] args)
		{
			this.Write (LogLevel.Debug, AddSender<TSender> (msg), args);
		}
		
		///<summary>
		/// Information of incidental value to the user.
		///</summary>
		public void Notice (string msg, params object [] args)
		{
			this.Write (LogLevel.Notice, AddSender (msg), args);
		}
		///<summary>
		/// Information of incidental value to the user.
		///</summary>
		public void Notice<TSender> (string msg, params object [] args)
		{
			this.Write (LogLevel.Notice, AddSender<TSender> (msg), args);
		}
		
		///<summary>
		/// A problem that can be handled without preventing the completion of the affected operation.
		///</summary>
		public void Warning (string msg, params object [] args)
		{
			this.Write (LogLevel.Warning, AddSender (msg), args);
		}
		///<summary>
		/// A problem that can be handled without preventing the completion of the affected operation.
		///</summary>
		public void Warning<TSender> (string msg, params object [] args)
		{
			this.Write (LogLevel.Warning, AddSender<TSender> (msg), args);
		}
		
		///<summary>
		/// An error that prevents an operation from completing successfully,
		/// but can be handled without affecting the process continued operation.
		///</summary>
		public void Error (string msg, params object [] args)
		{
			this.Write (LogLevel.Error, AddSender (msg), args);
		}
		///<summary>
		/// An error that prevents an operation from completing successfully,
		/// but can be handled without affecting the process continued operation.
		///</summary>
		public void Error<TSender> (string msg, params object [] args)
		{
			this.Write (LogLevel.Error, AddSender<TSender> (msg), args);
		}
		
		/// <summary>
		/// An error of sufficient magnitude that it cannot be handled.
		/// </summary>
		public void Fatal (string msg, params object [] args)
		{
			this.Write (LogLevel.Fatal, AddSender (msg), args);
		}
		/// <summary>
		/// An error of sufficient magnitude that it cannot be handled.
		/// </summary>
		public void Fatal<TSender> (string msg, params object [] args)
		{
			this.Write (LogLevel.Fatal, AddSender<TSender> (msg), args);
		}
	}
}

